﻿using System.Linq;
using UnityEngine;
using UnityEngine.EventSystems;

namespace Nomo.UnityAnimationModule
{
    /// <summary>
    /// 点击效果。
    /// </summary>
    public sealed class ClickEffect : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerEnterHandler, IPointerExitHandler
    {
        [SerializeField] private Transform[] targets   = new Transform[0];                           // 缩放的目标
        public                   Vector3     fromScale = Vector3.one;                                // 原来的局部缩放值
        public                   Vector3     toScale   = Vector3.one + (Vector3) Vector2.one * 0.2F; // 在按下后想要达到的局部缩放值
        [SerializeField] private bool        handleEnterAndExitEvents;                               // 是否处理“进入”和“离开”事件？

        private bool  _isPointerDown;
        private bool  _isPlaying;
        private float _t;

        void IPointerDownHandler.OnPointerDown(PointerEventData eventData)
        {
            _isPointerDown = true;
            _isPlaying     = true;
        }

        void IPointerUpHandler.OnPointerUp(PointerEventData eventData)
        {
            _isPointerDown = false;
            _isPlaying     = true;
        }

        void IPointerEnterHandler.OnPointerEnter(PointerEventData eventData)
        {
            if (!handleEnterAndExitEvents || eventData.pointerEnter != eventData.rawPointerPress)
            {
                return;
            }

            _isPointerDown = true;
            _isPlaying     = true;
        }

        void IPointerExitHandler.OnPointerExit(PointerEventData eventData)
        {
            if (!handleEnterAndExitEvents || eventData.pointerEnter != eventData.rawPointerPress)
            {
                return;
            }

            _isPointerDown = false;
            _isPlaying     = true;
        }

        private void EnsureValidState()
        {
            targets = targets.Where(IsNotNull).ToArray();

            if (targets.Length == 0)
            {
                targets = new[] {transform};
            }
        }

        private static bool IsNotNull(Transform t)
        {
            return t != null;
        }

        private void Init()
        {
            _isPointerDown = false;
            _isPlaying     = false;
            _t             = 0F;
            foreach (var element in targets)
            {
                element.localScale = fromScale;
            }
        }

        private void RefreshLocalScale()
        {
            var localScale = Vector3.Lerp(fromScale, toScale, _t);
            foreach (var element in targets)
            {
                element.localScale = localScale;
            }
        }

        private void OnEnable()
        {
            EnsureValidState();
            Init();
        }

        private void OnDisable()
        {
            EnsureValidState();
            Init();
        }

        private void Update()
        {
            if (!_isPlaying)
            {
                return;
            }

            _t = Mathf.MoveTowards(_t, _isPointerDown ? 1F : 0F, 0.15F);
            RefreshLocalScale();
            switch (_isPointerDown)
            {
                case true when _t  >= 1F:
                case false when _t <= 0F:
                    _isPlaying = false;
                    break;
            }
        }
    }
}
